package sh.grapendaal.tsuushin.service.impl;

import java.util.HashMap;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Singleton;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import sh.grapendaal.tsuushin.core.Command;
import sh.grapendaal.tsuushin.core.Context;
import sh.grapendaal.tsuushin.core.IrcBot;
import sh.grapendaal.tsuushin.core.RunnableCommand;
import sh.grapendaal.tsuushin.message.IncomingMessage;
import sh.grapendaal.tsuushin.message.MessageType;
import sh.grapendaal.tsuushin.service.CommandService;
import sh.grapendaal.tsuushin.service.PermissionService;

/**
 * The Command Service provides functionality to register a method which handles
 * a certain command triggered by a message from IRC. Commands are registered by
 * their trigger, which means the first word of a message. A trigger is prefixed
 * with a symbol to prevent normal sentences from triggering commands. The
 * prefix is configurable.
 */
@Singleton
public class CommandServiceImpl implements CommandService, IrcBot.ReceiveListener {
	private static final Logger LOGGER = LoggerFactory.getLogger(CommandServiceImpl.class);

	private static final int PREFIX_OFFSET = 1;

	@Inject
	private PermissionService permissionService;

	private char prefix;
	private Map<String, Command> commands = new HashMap<String, Command>();

	@Override
	public void setPrefix(char prefix) {
		this.prefix = prefix;
	}

	@Override
	public char getPrefix() {
		return this.prefix;
	}

	@Override
	public void registerCommand(String trigger, String permission, RunnableCommand callback) {
		if (commandExists(trigger)) {
			throw new IllegalArgumentException("A command with trigger \"" + trigger + "\" already exists.");
		}
		if (permission != null && !this.permissionService.permissionExists(permission)) {
			throw new IllegalArgumentException("The permission \"" + permission + "\" does not exist.");
		}
		this.commands.put(trigger, new Command(trigger, permission, callback));
	}

	@Override
	public void unregisterCommand(String trigger) {
		if (!commandExists(trigger)) {
			throw new IllegalArgumentException("A command with trigger \"" + trigger + "\" does not exist.");
		}
		this.commands.remove(trigger);
	}

	@Override
	public boolean commandExists(String trigger) {
		return this.commands.containsKey(trigger);
	}

	@Override
	public boolean onReceive(IrcBot bot, IncomingMessage message) {
		if (message.getMessageType() == MessageType.CHANNEL_MESSAGE
				&& message.getContent().charAt(0) == this.getPrefix()) {
			LOGGER.debug("Potential command received: {}", message.getContent());
			String[] chunks = message.getContent().split("\\s+");
			String trigger = chunks[0].substring(PREFIX_OFFSET);

			return triggerCommand(trigger, bot, message);
		}

		return true;
	}

	/**
	 * Checks if there is a command listening to the given trigger and if so,
	 * checks if the user can execute that command. If that's the case as well,
	 * then the command is executed.
	 * 
	 * @param trigger
	 *            The trigger to look for.
	 * @param context
	 *            The current context.
	 * @param message
	 *            The received message.
	 * @return Boolean indicating whether processing should continue.
	 */
	private boolean triggerCommand(String trigger, IrcBot bot, IncomingMessage message) {
		if (!commandExists(trigger)) {
			return true;
		}

		Command command = this.commands.get(trigger);
		if (!this.permissionService.hasPermission(message.getUser(), command.getPermission())) {
			return true;
		}

		Context context = new Context(bot, bot.getNetwork());
		return this.commands.get(trigger).execute(context, message);
	}
}
